Skip to content

Conversation

@AndyAyersMS
Copy link
Member

Emit plausible Wasm control flow for blocks during genCodeForBBList.

Also add a bit of support for relop codegen, so blocks ending in branches have something to use.

Contributes to #121178.

Emit plausible Wasm control flow for blocks during `genCodeForBBList`.

Also add a bit of support for relop codegen, so blocks ending in
branches have something to use.

Contributes to dotnet#121178.
Copilot AI review requested due to automatic review settings November 25, 2025 23:45
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Nov 25, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements WebAssembly control flow code generation during the genCodeForBBList phase, enabling the JIT to emit structured control flow for Wasm blocks, loops, and branches. The implementation includes basic relational operator (relop) support for conditional branches.

Key changes include:

  • Addition of Wasm control flow and relational operation instructions to the instruction table
  • Movement of WasmInterval class from fgwasm.cpp to fgwasm.h for use during code generation
  • Integration of the Wasm control flow transformation phases into the compilation pipeline (non-DEBUG builds)
  • Implementation of control flow instruction emission in genCodeForBBlist, including block/loop/end nesting and branch depth calculation
  • Basic GT_GT (greater than) relational operator codegen support

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
instrswasm.h Adds Wasm control flow instructions (block, loop, if, else, end, br, br_if, br_table, return) and relational operators (eqz, eq, ne, lt, gt, le, ge for i32, i64, f32, f64 types)
fgwasm.h Moves WasmInterval class definition from .cpp to header for codegen access
fgwasm.cpp Moves WasmInterval to header, adds block reordering with branch condition reversal, adds debug dump functions, removes debug-only control flow simulation code
jitconfigvalues.h Removes JitWasmControlFlow debug config option (now enabled for TARGET_WASM)
compiler.h Adds WasmInterval forward declaration and fgWasmIntervals field to store control flow intervals for codegen
compiler.cpp Moves Wasm SCC transformation and control flow phases from DEBUG-only to TARGET_WASM builds, repositions them in compilation pipeline
lowerwasm.cpp Implements LowerJTrue to defer branch handling to codegen instead of using NYI
emitwasm.h Adds code_t typedef for instruction opcodes
emitwasm.cpp Implements insOpcode lookup, emitOutputInstr for IF_OPCODE/IF_ULEB128 formats, emitJumpKindToIns mapping, placeholder getInsExecutionCharacteristics
emitjmps.h Defines Wasm jump kinds: br, br_if, br_table
codegenwasm.cpp Implements genFnProlog, adds GT_GT to tree node codegen, implements inst_JMP/inst_LABEL for Wasm depth-based branching
codegen.h Adds Wasm-specific inst_JMP and inst_LABEL declarations taking depth parameter
codegenlinear.cpp Adds Wasm control flow stack management, emits block/loop/end instructions around blocks, generates Wasm branches (br, br_if, br_table) at block ends with depth calculation
CMakeLists.txt Moves fgwasm.cpp from common JIT sources to TARGET_WASM-specific sources

@AndyAyersMS
Copy link
Member Author

FYI @dotnet/jit-contrib @SingleAccretion

This won't get much testing in CI... locally it lets us get a simple method like

int Foo(int a, int b)
{
    if (a > b) return b;
    return a;
}

through more of codegen. Prolog gen stubbed out for now.

=============== Generating BB01 [0000] [000..004) -> BB02(0.5),BB03(0.5) (cond), preds={} succs={BB03,BB02} flags=0x00000000.00000011: i LIR
BB01 IN (2)={V00 V01}
     OUT(2)={V00 V01}

Change life 0000000000000000 {} -> 0000000000000003 {V00 V01}
Debug: New V00 debug range: first
Debug: New V01 debug range: first

      L_M10749_BB01:
Mapped BB01 to G_M10749_IG02
IN0001:             block

Scope info: begin block BB01, IL range [000..004)
Added IP mapping: 0x0000 STACK_EMPTY (G_M10749_IG02,ins#1,ofs#1) label
Generating:                [000008] -----------                            IL_OFFSET void   INLRT @ 0x000[E--]
Generating: N001 (  1,  1) [000000] -----+-----                    t0 =    LCL_VAR   int    V00 arg0         u:1 $80
IN0002:             i32.load 1 4
Generating: N002 (  1,  1) [000001] -----+-----                    t1 =    LCL_VAR   int    V01 arg1         u:1 $81
IN0003:             i32.load 1 0
                                                                        /--*  t0     int    
                                                                        +--*  t1     int    
Generating: N003 (  3,  3) [000002] J----+-N---                    t2 = *  GT        int   
IN0004:             i32.gt_s
                                                                        /--*  t2     int    
Generating: N004 (  5,  5) [000003] -----+-----                         *  JTRUE     void   $VN.Void
IN0005:             br_if 0

...

Also addresses a few bits of @jakobbotsch's feedback on #121728. The SCC phase is moved later, some debug only stuff is now really debug only, etc.

@am11 am11 added the arch-wasm WebAssembly architecture label Nov 26, 2025
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

@AndyAyersMS
Copy link
Member Author

With the latest commit, we can alt-jit methods that just return. Lots of stuff still incomplete, but we have a lot we can fill in now.

image

@AndyAyersMS
Copy link
Member Author

@SingleAccretion think I got most of your feedback, except leaving LABEL as is for now. Wasn't sure what you were commenting on for the prolog.

Please take another look.

Copy link
Contributor

@SingleAccretion SingleAccretion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing the feedback, it looks much nicer now! I've left a few comments.

@AndyAyersMS
Copy link
Member Author

@SingleAccretion thanks again, pushed another update

Copy link
Contributor

@SingleAccretion SingleAccretion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo the genFnProlog comment (#121973 (comment)) and one suggestion.

@AndyAyersMS
Copy link
Member Author

@dotnet/jit-contrib ping

@AndyAyersMS
Copy link
Member Author

@jakobbotsch now using a common genEmitEndBlock.
@adamperlin any other notes you want to add?

Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me now.

@AndyAyersMS
Copy link
Member Author

Not easy to get Roslyn to emit an actual switch, but here's an example of one making it to codegen (requires some fixes/workarounds not in this PR)

image

Copy link
Contributor

@adamperlin adamperlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me! I don't have any notes I wanted to add at the moment!

@AndyAyersMS AndyAyersMS merged commit 75eb218 into dotnet:main Dec 4, 2025
117 of 123 checks passed
agocke added a commit that referenced this pull request Dec 5, 2025
#122171)

HostActivation tests are hitting the 5-minute hang dump timeout on OSX
CI despite making steady progress. Tests complete in ~2 minutes locally
but CI environments run 2x slower.

**Changes:**
- Increased `--hangdump-timeout` from `5m` to `8m` in
`src/installer/tests/Directory.Build.props`

This provides adequate headroom for slower CI machines while still
catching actual hangs.

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>Host Activation test failures on OSX</issue_title>
> <issue_description>## Build Information
> Build:
https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1219628
> Build error leg or test failing:
HostActivation.Tests.WorkItemExecution
> Pull request: #121973
> <!-- Error message template  -->
> ## Error Message
> 
> Fill the error message using [step by step known issues
guidance](https://github.com/dotnet/arcade/blob/main/Documentation/Projects/Build%20Analysis/KnownIssueJsonStepByStep.md).
> 
> <!-- Use ErrorMessage for String.Contains matches. Use ErrorPattern
for regex matches (single line/no backtracking). Set BuildRetry to
`true` to retry builds with this error. Set ExcludeConsoleLog to `true`
to skip helix logs analysis. -->
> 
> ```json
> {
>   "ErrorMessage": "Hang dump timeout of '00:05:00' expired",
>   "ErrorPattern": "",
>   "BuildRetry": false,
>   "ExcludeConsoleLog": false
> }
> ```
> 
> 
> <!-- Known issue validation start -->
>  ### Known issue validation
> **Build: :mag_right:**
https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219628
> **Error message validated:** `[Hang dump timeout of '00:05:00'
expired`]
> **Result validation:** :white_check_mark: Known issue matched with the
provided build.
> **Validation performed at:** 12/4/2025 3:07:33 AM UTC
> <!-- Known issue validation end -->
> <!--Known issue error report start -->
> 
> ### Report
> 
> |Build|Definition|Test|Pull Request|
> |---|---|---|---|
>
|[1219965](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219965)|dotnet/runtime|[HostActivation.Tests.WorkItemExecution](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219965&view=ms.vss-test-web.build-test-results-tab&runId=33790538&resultId=100095)|dotnet/runtime#122136|
>
|[1219914](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219914)|dotnet/runtime|[HostActivation.Tests.WorkItemExecution](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219914&view=ms.vss-test-web.build-test-results-tab&runId=33788030&resultId=100095)|dotnet/runtime#117148|
>
|[1219763](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219763)|dotnet/runtime|[HostActivation.Tests.WorkItemExecution](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219763&view=ms.vss-test-web.build-test-results-tab&runId=33785738&resultId=100095)|dotnet/runtime#122125|
>
|[1219716](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219716)|dotnet/runtime|[HostActivation.Tests.WorkItemExecution](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219716&view=ms.vss-test-web.build-test-results-tab&runId=33780760&resultId=100095)|dotnet/runtime#122168|
>
|[1219709](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219709)|dotnet/runtime|[HostActivation.Tests.WorkItemExecution](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219709&view=ms.vss-test-web.build-test-results-tab&runId=33780492&resultId=100095)|dotnet/runtime#122163|
>
|[1219628](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219628)|dotnet/runtime|[HostActivation.Tests.WorkItemExecution](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1219628&view=ms.vss-test-web.build-test-results-tab&runId=33778378&resultId=100095)|dotnet/runtime#121973|
> #### Summary
> |24-Hour Hit Count|7-Day Hit Count|1-Month Count|
> |---|---|---|
> |6|6|6|
> <!--Known issue error report end --></issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> <comment_new><author>@agocke</author><body>
> My PR almost certainly broke this, but I'm not sure how
yet</body></comment_new>
> <comment_new><author>@agocke</author><body>
> Actually, I'm not sure I "broke" this in a real sense -- right now the
hang dump timeout is kicking in at the 5m mark. But the log shows a
steady stream of tests passing all the way up to the 5m mark.
> 
> I wonder if this is just that the tests are taking longer than 5m to
finish and the hang dump timeout is measuring from the start of the test
run.</body></comment_new>
> <comment_new><author>@agocke</author><body>
> Yeah my uncontended M2 Mac takes 2 min to run the tests, I could
easily believe CI is twice as slow. I think we should bump this to ten
minutes.</body></comment_new>
> </comments>
> 


</details>

- Fixes #122169

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: agocke <[email protected]>
adamperlin added a commit that referenced this pull request Dec 8, 2025
This is the first prototype of a Wasm object writer for crossgen that
*only* writes out method bodies. It builds the minimum module structure
which is needed to declare and export the module bodies so that the
resulting module can be loaded and method bodies can be called by an
external loader. I have tested the output with a simple JavaScript
loader that instantiates the module and calls one of the exports.

# What IS handled
Emitting simple method bodies from an assembly that have no relocations
or external dependencies of any kind.

# What is NOT handled
## Relocations and any metadata attached to compiled methods.
## R2R format envelope (the plan is to embed this in the data section in
a future PR)
R2R metadata is currently unused.
## Signature generation to meet ABI requirements
Right now, there is a single `i32 -> i32` signature which is used as a
placeholder for any method. Later work will need to build support for
generating and storing wasm-level type signatures for JIT compiled
methods.
## JIT Integration
Currently, the JIT is not called (I believe it will hit asserts on the
Wasm target until #121973 is in). Instead, a simple `(i32.const 0)
(return)` stub is emitted for any JIT calls.

---------

Co-authored-by: Adeel Mujahid <[email protected]>
Co-authored-by: Copilot <[email protected]>
@github-actions github-actions bot locked and limited conversation to collaborators Jan 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants